#include "mutexfile.h"

/**
 * @fn void readUnLock_MutexFile(MutexFile *mutexFile)
 * @brief Fonction pour débloquer l'écriture lorsque tout les lecteurs ont finis de lire.
 *
 * @param[in,out] mutexFile Mutex de travail.
 */
void readUnLock_MutexFile(MutexFile *mutexFile);

/**
 * @fn void readLock_MutexFile(MutexFile *mutexFile)
 * @brief Fonction pour bloquer l'écriture lorsque un lecteur souhaite lire.
 *
 * @param[in,out] mutexFile Mutex de travail.
 */
void readLock_MutexFile(MutexFile *mutexFile);

void initialize_MutexFile(MutexFile *mutexFile)
{
  initialize_List(&(mutexFile->listFileUse), create_SocketFile, delete_SocketFile, compare_SocketFile, NULL);
  pthread_mutex_init(&(mutexFile->mutexWrite), NULL);
  pthread_mutex_init(&(mutexFile->mutexRead), NULL);
  pthread_mutex_init(&(mutexFile->mutexEquity), NULL);
  pthread_cond_init(&(mutexFile->condWrite), NULL);
  pthread_cond_init(&(mutexFile->condRead), NULL);
  mutexFile->cptRead = 0;
}

void testament_MutexFile(MutexFile *mutexFile)
{
  mutexFile->cptRead = 0;
  pthread_mutex_destroy(&(mutexFile->mutexWrite));
  pthread_mutex_destroy(&(mutexFile->mutexRead));
  pthread_mutex_destroy(&(mutexFile->mutexEquity));
  pthread_cond_destroy(&(mutexFile->condWrite));
  pthread_cond_destroy(&(mutexFile->condRead));
  testament_List(&(mutexFile->listFileUse));
}

MutexFileReturn tryInsert_MutexFile(MutexFile *mutexFile, const Element element)
{
  MutexFileReturn ret = MUTEXFILE_SUCCESS;
  pthread_mutex_lock(&(mutexFile->mutexEquity));
  pthread_mutex_lock(&(mutexFile->mutexWrite));

  if(findElement_List(&(mutexFile->listFileUse), element) == LIST_NOT_FOUND)
    {
      insertLast_List(&(mutexFile->listFileUse), element);
    }
  else
    ret = MUTEXFILE_INSIDE;
  pthread_mutex_unlock(&(mutexFile->mutexWrite));
  pthread_mutex_unlock(&(mutexFile->mutexEquity));
  return ret;
}

void readLock_MutexFile(MutexFile *mutexFile)
{
  pthread_mutex_lock(&(mutexFile->mutexEquity));
  pthread_mutex_lock(&(mutexFile->mutexRead));
  mutexFile->cptRead ++;
  if(mutexFile->cptRead == 1)
    pthread_mutex_lock(&(mutexFile->mutexWrite));
  pthread_mutex_unlock(&(mutexFile->mutexRead));
  pthread_mutex_unlock(&(mutexFile->mutexEquity));
}

void readUnLock_MutexFile(MutexFile *mutexFile)
{
  pthread_mutex_lock(&(mutexFile->mutexRead));
  mutexFile->cptRead --;
  if(mutexFile->cptRead == 0)
    pthread_mutex_unlock(&(mutexFile->mutexWrite));
  pthread_mutex_unlock(&(mutexFile->mutexRead));
}

MutexFileReturn isInside_MutexFile(MutexFile *mutexFile, const Element element)
{
  MutexFileReturn ret = MUTEXFILE_INSIDE;
  readLock_MutexFile(mutexFile);

  if(findElement_List(&(mutexFile->listFileUse), element) == LIST_NOT_FOUND)
    ret = MUTEXFILE_NOT_INSIDE;

  readUnLock_MutexFile(mutexFile);
  return ret;
}

MutexFileReturn delete_MutexFile(MutexFile *mutexFile, const Element element)
{
  MutexFileReturn ret = MUTEXFILE_SUCCESS;
  pthread_mutex_lock(&(mutexFile->mutexEquity));
  pthread_mutex_lock(&(mutexFile->mutexWrite));
  mutexFile->listFileUse.compare = compareDelete_SocketFile;
  if(findElement_List(&(mutexFile->listFileUse), element) == LIST_NOT_FOUND)
    {
      ret = MUTEXFILE_NOT_INSIDE;
    }
  else
    {
      ListReturn lret;
      lret = deleteCurrent_List(&(mutexFile->listFileUse));
      if(lret != LIST_SUCCESS)
	ret = MUTEXFILE_ERR;
    }
  mutexFile->listFileUse.compare = compare_SocketFile;
  pthread_mutex_unlock(&(mutexFile->mutexWrite));
  pthread_mutex_unlock(&(mutexFile->mutexEquity));
  return ret;
}
